第八日:從文件走向程式,Provisioning 工地正式開張 🚧
昨天還在紙上談兵,今天終於把藍圖變成鋼筋水泥。第八天,我跟 Codex 不再只是畫圖、寫文件,而是真正下場「寫程式 + 跑測試」。整個工地總算開始冒煙了。以下是今天完整的紀錄。
一、今日戰場回顧:從文件到程式碼
昨天,我們已經把需求文件、API 契約、資料模型都寫好,算是蓋了一間樣品屋。今天的任務,就是把這些樣品拆掉,蓋一個真的能通電、能跑測試的Code。
根據今天的開發日誌,主要做了五件事:
- 建立 Go 模組與目錄結構,包含 service、HTTP handler、記憶體儲存層。
- 定義 US-001 的資料流:payload → notification key profile → destination grants → delivery token。
- 撰寫單元測試,涵蓋成功案例、idempotency、驗證失敗、quota 超標、目的地錯誤。
- 本地跑
go test ./...
,全部測試通過 🎉。
- 寫好
curl
範例,直接打 API,驗證成功案例與錯誤案例。
一句話總結:今天不是「我覺得可以跑」,而是「真的能跑」。
二、程式碼工地巡禮
1. 記憶體儲存層 MemoryStore
一開始先有一個臨時倉庫,名字叫 MemoryStore
。它就像工地的臨時鐵皮屋,雖然簡陋,但能放下所有工地器材。
- 功能包括:儲存 payload、profiles、provisioning 結果、destination catalog、grants、audit log、delivery token。
- 提供驗證目的地是否存在、是否 active。
- 支援快照,讓測試時能直接抓目前 catalog 與 profiles 狀態。
關鍵設計:用 compositeKey(externalSystemID, approvalID)
當唯一索引,確保 idempotency。這保證「同樣的審核單重複送」不會創造兩份結果。
2. Service 層 Provisioner
然後是整個工地的施工主管 —— Provisioner
。它負責 orchestrate 流程:
- 驗證輸入(缺欄位、email 格式錯誤、quota 超標、沒有目的地,全都會被攔下)。
- 確認 idempotency(如果同一個 approval_id 已經成功過,就直接回傳舊結果,並打上
Existing=true
)。
- 儲存 payload。
- 驗證 destinations 是否存在於 catalog。
- 生成一堆 identifier(notification key、API key、delivery token、audit trace)。
- 儲存 profile 與 destination grants。
- 儲存 delivery token。
- Persist 結果,並寫入 audit trail。
整個 Provisioner 就像是「現場監工」,一條龍服務。
3. HTTP Handler
接下來就是讓外部能打 API 的 HTTP handler。
- Endpoint:
POST /internal/v1/provisioning/notification-keys
- 驗證 JSON payload,不允許未知欄位。
- Metadata 僅允許 string 或 number(若傳物件就直接 400)。
- 呼叫 Provisioner,回傳結果(201 Created)。
- 針對錯誤類型,回傳對應 HTTP 狀態:
- Invalid request → 400
- Quota 超標 → 422
- Destination not found → 422
- 其他錯誤 → 500
這讓 API 用起來更「雲原生感」,而不是只有一個「500,自己看 log」。
4. Domain Model
在 model.go
,定義了所有核心結構:
-
ProvisionRequest
:API payload。
-
ProvisionResult
:API 回傳結果。
-
NotificationKeyProfile
:API key profile。
-
DestinationRequest / DestinationGrant
:目的地相關。
-
QuotaConfig
:流量管制。
這些結構就像是整個專案的「混凝土」。沒有它們,整個工地就會倒。
5. Main 啟動程式
最後是 main.go,啟動一個 HTTP server,掛上 handler。並支援環境變數設定 PROVISIONING_MAX_REQUESTS_PER_MINUTE
,可以動態調整限流。
這個 server 現在可以本地跑起來,對外回應 201 或 422,算是進入「能 demo」的階段。
三、單元測試:給工地戴安全帽
光蓋工地還不夠,還要檢查每個工人有沒有戴安全帽。
今天加了完整的單元測試:
-
成功案例:正確的 payload → 201 Created,且回傳正確的 notification key 與目的地授權。
-
Idempotency:同一個 request 重送,應該重用舊的 notification key,並打上
Existing=true
。
-
Invalid Request:缺欄位或 email 格式錯誤 → 400。
-
Quota Exceeded:超過平台 policy → 422。
-
Destination Validation Error:destination 不存在或 inactive → 422。
-
Invalid Metadata:metadata 傳 nested object → 400。
全部測試通過,這讓我心裡更安心,不怕 demo 的時候爆炸。
四、curl 實測:工地通電啦
最後一步是「通電驗收」。今天寫了 curl
測試案例:
- 成功案例:正確 payload → 回傳一組 notification key、delivery token。
- 錯誤案例:缺少目的地 → 422,提示
DESTINATION_NOT_FOUND
。
測完之後,我才敢宣告:工地真的能運轉。
五、今日心得:AI 共筆 = 加速器,但不是萬能
今天最大的體會:AI 不只是幫忙寫文件,它也能幫忙寫程式碼,甚至測試。但它的角色比較像「加速器」,而不是「取代工程師」。
舉幾個例子:
-
AI 的速度:一開始我只丟需求,它很快就幫我生出 HTTP handler skeleton,少打了兩百行樣板程式碼。
-
AI 的盲點:它會寫一些「過於完美」的 code,例如錯誤處理直接用 500,沒有考慮 domain-specific 狀況。我需要自己補上。
-
AI 的好用之處:幫忙生成測試,尤其是 payload JSON,手動寫很累,它三秒鐘就搞定。
總結:AI 幫忙省下 50% 的樣板時間,但設計、邏輯、驗證還是得自己來。
六、明日計畫:從 Prototype 到 Platform
今天的工地已經「能跑」。明天的方向:
-
補真實儲存層:不再只用 MemoryStore,要改用 DB。
-
補發 API:delivery token 過期,要有 re-issue 機制。
-
Destination Catalog 同步策略:目前是 seed,未來要跟 Teams 動態同步。
-
更完整的測試:增加 integration test,模擬實際 HTTP call。
一句話:從「能跑」走向「能用」。
七、結語:從文件藍圖到工地煙火
第八天,我終於能驕傲地說:我們有一個能運作的 Provisioning API prototype。
- 文件 → 程式碼 → 測試 → 驗收,全都跑完一輪。
- 雖然還是 MemoryStore,但至少不再只是紙上建築。
- AI 在過程中給了極大助力,讓我更快到達這一步。
未來還有很多挑戰,但今天,這間工地終於開張、點燈、能 demo。
這就是鐵人賽 Day 8 的最大收穫。
👉 明天再戰,繼續搬磚! 🚀